package com.jingtuo.java.practice;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Server-Send Events
 *
 * 参考资料: https://html.spec.whatwg.org/multipage/server-sent-events.html#garbage-collection
 */
public class EventSource {

    private static final String EMPTY_LINE = "";

    private static final String COLON = ":";

    private static final String NAME_EVENT = "event";

    private static final String NAME_DATA = "data";

    private static final String NAME_ID = "id";

    private static final String DATA_SPLIT = "\r\n";

    private static final String EVENT_MESSAGE = "message";

    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    private static final String HEADER_LAST_EVENT_ID = "Last-EVENT-ID";

    private static final String CONTENT_TYPE_EVENT_STREAM = "text/event-stream;charset=UTF-8";

    private static final int STATE_CONNECTING = 0;

    private static final int STATE_OPEN = 1;

    private static final int STATE_CLOSE = 2;

    private static final int STATE_NONE = -1;

    private String url;

    private int connectTimeOut = 5000;

    private String lastEventId;

    private int state = STATE_NONE;

    private HttpURLConnection urlConnection;

    public EventSource(String url) {
        this.url = url;
    }

    public void start() {
        try {
            state = STATE_CONNECTING;
            URL url = new URL(this.url);
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setDoInput(true);
            urlConnection.setDoOutput(true);
            urlConnection.setRequestMethod("POST");
//            urlConnection.addRequestProperty("Accept", CONTENT_TYPE_EVENT_STREAM);
            urlConnection.addRequestProperty("Connection", "Keep-Alive");
            urlConnection.addRequestProperty("Cache-Control", "no-cache");
            urlConnection.addRequestProperty("Content-Type","application/json");
//            urlConnection.addRequestProperty("Content-Type","application/x-www-form-urlencoded");
            urlConnection.addRequestProperty("DeviceID","YBuQZ4C/WE4DAMIP0lRjWYER");
            urlConnection.addRequestProperty("User-Agent","xf/8.3.0 (VOG-AL00;Android 10)");
            urlConnection.addRequestProperty("Op-Station","XF2,MP:13030303030,MAC:02:00:00:00:00:00,uuid/imei:YBuQZ4C/WE4DAMIP0lRjWYER,OS:Android,Ver:8.3.0,IP:10.150.13.150");
            urlConnection.addRequestProperty("Op-uToken","6Ig/HzG7xnwOVVSJaEF4G8F9gyKMOrUrj4CDcNTw9t6QbsAGO4Uk/LHKfgQGOy3f/ks40IHMSznFIbPTBf9RKavkc1fwMHCfm8cvJM/ZU7pfChey4nS4vop/4+DIWBmg5Ei/X++kN7ZPRS/LzOyz7FUSS36IwCBDARHvlP3qdDo=");
            urlConnection.setUseCaches(false);
            if (lastEventId != null && lastEventId.isEmpty() && lastEventId.trim().isEmpty()) {
                urlConnection.addRequestProperty(HEADER_LAST_EVENT_ID, lastEventId);
            }
            urlConnection.setConnectTimeout(connectTimeOut);
            //读数据无超时限制
            urlConnection.setReadTimeout(60000);

            urlConnection.connect();

            DataOutputStream dos = new DataOutputStream(urlConnection.getOutputStream());
            String dataStr = "{'requests':[{'header':{'aliveToken':'2Hj+7QfhHfkoehoTuond5Rd483VRq87kqOOwgBgXQbWLUusC2KuDn6C9gbN/bkJ5trdgcwcOaEQ\\u003d'},'id':'level2-refresh','interval':600,'method':'POST','scope':'private'}]}";
            dos.writeBytes(dataStr);
            dos.flush();
            dos.close();

            boolean isEventStream = false;
            int responseCode = urlConnection.getResponseCode();
            Map<String, List<String>> headers = urlConnection.getHeaderFields();
            for (String key: headers.keySet()) {
                System.out.println("\t" + key + ":");
                List<String> values = headers.get(key);
                if (HEADER_CONTENT_TYPE.equalsIgnoreCase(key)) {
                    isEventStream = values.contains(CONTENT_TYPE_EVENT_STREAM);
                }
                for (String value: values) {
                    System.out.println("\t\t" + value);
                }
            }
            if (200 == responseCode && isEventStream) {
                //仅处理请求是200, 并且响应头包含CONTENT_TYPE_EVENT_STREAM;
                BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
                Event event = null;
                while (true) {
                    String line = reader.readLine();
                    if (line == null) {
                        break;
                    }
                    System.out.println(line);
                    if (line.startsWith(COLON)) {
                        //注解
                        System.out.println("发现注释: " + line);
                        continue;
                    }
                    if (line.equals(EMPTY_LINE)) {
                        if (event != null) {
                            //一个事件结束, 下发事件
                            if (event.getEvent() == null || event.getEvent().isEmpty() || event.getEvent().trim().isEmpty()) {
                                event.setEvent(EVENT_MESSAGE);
                            }
                            //更新lastEventId, 便于下次接着推送
                            lastEventId = event.getId();
                            System.out.println("一个事件结束");
                            event = null;
                        }
                        continue;
                    }
                    String name = "";
                    String value = "";
                    if (line.contains(COLON)) {
                        int index = line.indexOf(COLON);
                        name = line.substring(0, index);
                        //如果冒号后面第一个是空格要移除
                        value = line.substring(index + 1).trim();
                    } else {
                        name = line;
                    }
                    if (event == null) {
                        event = new Event();
                    }
                    if (NAME_EVENT.equals(name)) {
                        event.setEvent(value);
                    } else if (NAME_ID.equals(name)) {
                        event.setId(value);
                    } else if (NAME_DATA.equals(name)) {
                        String oldData = event.getData();
                        if (oldData == null) {
                            oldData = value;
                        } else {
                            oldData += DATA_SPLIT + value;
                        }
                        event.setData(oldData);
                    } else {
                        Map<String, String> otherFields = event.getOtherFields();
                        if (otherFields == null) {
                            otherFields = new HashMap<>();
                            event.setOtherFields(otherFields);
                        }
                        String oldValue = otherFields.get(name);
                        if (oldValue == null) {
                            oldValue = value;
                            otherFields.put(name, oldValue);
                        } else {
                            oldValue += DATA_SPLIT + value;
                            otherFields.put(name, oldValue);
                        }
                    }
                }
                reader.close();
            }
            close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public int getState() {
        return state;
    }


    public void close() {
        try {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            state = STATE_CLOSE;
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}
